from Graphics import pygame, SCREENDIM, Screen
import math, random, re

def drawActors():
    return True

################################################################################################################################################################3
class Asteroid(pygame.sprite.Sprite):
    def __init__(self, screen, spawnLoc):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = screen.load_image("asteroidBig01.png")
        
        self.position = spawnLoc
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        
    def update(self, screen, shipPosition):
        currentGridLoc = (SCREENDIM.width / 2 - shipPosition[0] % 500, SCREENDIM.height / 2 - shipPosition[1] % 500)
        
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        shipGrid = (int(shipPosition[0] / 500), int(shipPosition[1] / 500))
        
        #If the object is not adjacent to the player's grid,
        #it does not need to be rendered.
        whichGrid = self.findAdjacency(shipGrid)
        
        if whichGrid != None:
            #Then we need to render the object to the screen.
            #Add the relative grid (from whichGrid) to the ship's grid
            #and the grid offset for the object.
            offset = (self.position[0] % 500 - self.rect.width / 2, self.position[1] % 500 - self.rect.height / 2)
            currentGridLoc = (currentGridLoc[0] + whichGrid[0] * 500 + offset[0], currentGridLoc[1] + whichGrid[1] * 500 + offset[1])
            
            #screen.blit(self.image, currentGridLoc)
            
            self.rect.x = currentGridLoc[0]
            self.rect.y = currentGridLoc[1]
            
            return self
        return None
        
    def findAdjacency(self, shipGrid):
        #What we want to do is check the nine grids present
        #on the screen, and if the grid that the ship is
        #currently on is the same as or adjacent to the
        #object's grid, then return that adjustment.
        
        #This will return the grid to render the object to
        #with respect to the ship's grid
        for x in range(-1, 2):
            for y in range(-1, 2):
                if self.grid == (shipGrid[0] + x, shipGrid[1] + y):
                    return (x, y)
        return None
###################################################################################################################################################    
class Missile(pygame.sprite.Sprite):
    def __init__(self, screen, spawnLoc, ship_degree, hasSpeedBoost = False):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = screen.load_image("Projectile1.png")
        
        self.angle = ship_degree
        if(hasSpeedBoost == False):
            self.speed = 20
        else:
            self.speed = 20 + 20 # normal speed + speed boost
            
        self.lifetime = 0 #To kill it if it misses its target
        
        self.position = spawnLoc
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
    
    def update(self, screen, shipPosition):
        currentGridLoc = (SCREENDIM.width / 2 - shipPosition[0] % 500, SCREENDIM.height / 2 - shipPosition[1] % 500)
        
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        shipGrid = (int(shipPosition[0] / 500), int(shipPosition[1] / 500))
        
        #If the object is not adjacent to the player's grid,
        #it does not need to be rendered.
        whichGrid = self.findAdjacency(shipGrid)
        
        if whichGrid != None:
            #Then we need to render the object to the screen.
            #Add the relative grid (from whichGrid) to the ship's grid
            #and the grid offset for the object.
            offset = (self.position[0] % 500 - self.rect.width / 2, self.position[1] % 500 - self.rect.height / 2)
            currentGridLoc = (currentGridLoc[0] + whichGrid[0] * 500 + offset[0], currentGridLoc[1] + whichGrid[1] * 500 + offset[1])
            
            #screen.blit(self.image, currentGridLoc)
            
            self.rect.x = currentGridLoc[0]
            self.rect.y = currentGridLoc[1]
            
            return self
        return None 
        
        
    def findAdjacency(self, shipGrid):
        #What we want to do is check the nine grids present
        #on the screen, and if the grid that the ship is
        #currently on is the same as or adjacent to the
        #object's grid, then return that adjustment.
        
        #This will return the grid to render the object to
        #with respect to the ship's grid
        for x in range(-1, 2):
            for y in range(-1, 2):
                if self.grid == (shipGrid[0] + x, shipGrid[1] + y):
                    return (x, y)
        return None
    
    
    def rotate(self, degrees):
        self.angle = (self.angle + degrees) % 360
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        
            
    #Please use update(), below
    #It should contain everything that update_position does,
    #plus boundary checking and grid tracking
    def update_position(self):
        scale_x = math.cos(math.radians(self.angle))
        scale_y = math.sin(math.radians(self.angle))
        
        velocity_x = (self.speed * scale_y)
        velocity_y = (self.speed * scale_x)
        
        self.position = [self.position[0] - velocity_x, self.position[1] - velocity_y] #backwards -, +
        self.lifetime += 1
    def collision(self, screen, missile_position, sprite):        
        if sprite.position == self.position:
            sprite.kill()
            #load explosion image or movie
            print("collision true!")
            return True
        else:
            return False
#####################################################################################################################################################################        
class EnemyShip(pygame.sprite.Sprite):
    def __init__(self, screen, spawnLoc):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = screen.load_image("ship3_1.gif")
        
        self.original_image = self.image
        
        self.speed = 0
        self.angle = 0

        self.position = spawnLoc
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        
    def update(self, screen, shipPosition):
        currentGridLoc = (SCREENDIM.width / 2 - shipPosition[0] % 500, SCREENDIM.height / 2 - shipPosition[1] % 500)
        
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        shipGrid = (int(shipPosition[0] / 500), int(shipPosition[1] / 500))
        
        #If the object is not adjacent to the player's grid,
        #it does not need to be rendered.
        whichGrid = self.findAdjacency(shipGrid)
        
        if whichGrid != None:
            #Then we need to render the object to the screen.
            #Add the relative grid (from whichGrid) to the ship's grid
            #and the grid offset for the object.
            offset = (self.position[0] % 500 - self.rect.width / 2, self.position[1] % 500 - self.rect.height / 2)
            currentGridLoc = (currentGridLoc[0] + whichGrid[0] * 500 + offset[0], currentGridLoc[1] + whichGrid[1] * 500 + offset[1])
        
            #screen.blit(self.image, currentGridLoc)
            
            self.rect.x = currentGridLoc[0]
            self.rect.y = currentGridLoc[1]  
             
            return self
        return None 
        
    def findAdjacency(self, shipGrid):
        #What we want to do is check the nine grids present
        #on the screen, and if the grid that the ship is
        #currently on is the same as or adjacent to the
        #object's grid, then return that adjustment.
        
        #This will return the grid to render the object to
        #with respect to the ship's grid
        for x in range(-1, 2):
            for y in range(-1, 2):
                if self.grid == (shipGrid[0] + x, shipGrid[1] + y):
                    return (x, y)
        return None
    
    def rotate(self, degrees):
        self.angle = (self.angle + degrees) % 360
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        
    def accelerate(self):
        self.speed += 1
        if(self.speed > self.max_speed):
            self.speed = self.max_speed
        
    def decelerate(self):
        self.speed -= 1
        if(self.speed < -self.max_speed):
            self.speed = -self.max_speed
            
    #Please use update(), below
    #It should contain everything that update_position does,
    #plus boundary checking and grid tracking
    def update_position(self):
        scale_x = math.cos(math.radians(self.angle))
        scale_y = math.sin(math.radians(self.angle))
        
        velocity_x = (self.speed * scale_y)
        velocity_y = (self.speed * scale_x)
        
        self.position = [self.position[0] - velocity_x, self.position[1] - velocity_y] #backwards -, +
        
    def rotateToPlayer(self, ship_position):
        
        eshipX = self.position[0]
        eshipY = self.position[1]
        
        pshipX = ship_position[0]
        pshipY = ship_position[1]
        
        if self.angle!= math.atan2(pshipX - eshipX, pshipY - eshipY) *(360/math.pi):
            self.angle = math.atan2(pshipX - eshipX, pshipY - eshipY) *(360/math.pi)
            self.rotate(self.angle)
        else:
            pass
        
    def spawn(self, screen,  ship_position):
        self.update(screen, ship_position)
        #life = random.randint(1,3)
        #if life == 1:
        #    self.live1(ship_position)
        #elif life == 2:
        #    self.live2(ship_position)
        self.live1(ship_position)
        #self.live2(ship_position)
    
    def live1(self,  ship_position):
        """The ship will locate the player and attempt to ram into it. The speed and acceleration will be 1.1 of the players."""
        numOfAcceleration = random.randint(-2, 5)
        self.speed += numOfAcceleration
        max_speed = 5
        min_speed = -2
        if self.speed > max_speed:
            self.speed = max_speed
        if self.speed < min_speed:
            self.speed = min_speed
        self.update_position()
        self.rotateToPlayer(ship_position)
        
    def live2(self, ship_position):
        numOfAcceleration = random.randint(-2, 5)
        self.speed += numOfAcceleration
        max_speed = 8
        min_speed = -2
        if self.speed > max_speed:
            self.speed = max_speed
        if self.speed < min_speed:
            self.speed = min_speed
            
        distanceFromPlayer = math.sqrt((self.position[0] - ship_position[0])**2 + (self.position[1] - ship_position[1])**2)
        if distanceFromPlayer > 100:
            self.update_position()
            self.rotateToPlayer(ship_position)
        else:
            self.rotateToPlayer(ship_position)
            #shoot missile

        
    def displayData2(self, screen, collide):
        if pygame.font:
            font = pygame.font.Font(None, 20)
            self.screen = pygame.display.get_surface()
        
            #Collide
            if collide == True:
                text = font.render("Player is dead", 1, (255, 255, 255))
                textpos = text.get_rect(right=self.screen.get_width(), top=font.get_linesize()*3)
                self.screen.blit(text, textpos)
            return True
        else:
            return False
#########################################################################################################################################################################        
class PowerUpSpeed(pygame.sprite.Sprite):
    def __init__(self, screen, spawnLoc):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = screen.load_image("powerUpSpeed.png")
        self.name = "speed"
        self.life = 5000 # 5 seconds
        self.picked_up = 0
        
        self.position = spawnLoc
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        
    def update(self, screen, shipPosition):
        currentGridLoc = (SCREENDIM.width / 2 - shipPosition[0] % 500, SCREENDIM.height / 2 - shipPosition[1] % 500)
        
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        shipGrid = (int(shipPosition[0] / 500), int(shipPosition[1] / 500))
        
        #If the object is not adjacent to the player's grid,
        #it does not need to be rendered.
        whichGrid = self.findAdjacency(shipGrid)
        
        if whichGrid != None:
            #Then we need to render the object to the screen.
            #Add the relative grid (from whichGrid) to the ship's grid
            #and the grid offset for the object.
            offset = (self.position[0] % 500 - self.rect.width / 2, self.position[1] % 500 - self.rect.height / 2)
            currentGridLoc = (currentGridLoc[0] + whichGrid[0] * 500 + offset[0], currentGridLoc[1] + whichGrid[1] * 500 + offset[1])
            
            #screen.blit(self.image, currentGridLoc)
            
            self.rect.x = currentGridLoc[0]
            self.rect.y = currentGridLoc[1]
             
            return self
        return None  
        
        
    def findAdjacency(self, shipGrid):
        #What we want to do is check the nine grids present
        #on the screen, and if the grid that the ship is
        #currently on is the same as or adjacent to the
        #object's grid, then return that adjustment.
        
        #This will return the grid to render the object to
        #with respect to the ship's grid
        for x in range(-1, 2):
            for y in range(-1, 2):
                if self.grid == (shipGrid[0] + x, shipGrid[1] + y):
                    return (x, y)
        return None
    
    def set_picked_up(self, ticks):
        self.picked_up = ticks
        self.kill()
        
    def is_consumed(self, current_ticks):
        # print((current_ticks - self.picked_up), self.life)
        
        return ((current_ticks - self.picked_up) > self.life)
 
    
#########################################################################################################################################################################        
class PowerUpWeapon(pygame.sprite.Sprite):
    def __init__(self, screen, spawnLoc):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = screen.load_image("powerUpWeapon.png")
        self.name = "weapon"
        self.life = 20000 # 20 seconds
        self.picked_up = 0
        
        self.position = spawnLoc
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        
    def update(self, screen, shipPosition):
        currentGridLoc = (SCREENDIM.width / 2 - shipPosition[0] % 500, SCREENDIM.height / 2 - shipPosition[1] % 500)
        
        shipGrid = (int(shipPosition[0] / 500), int(shipPosition[1] / 500))
        
        #If the object is not adjacent to the player's grid,
        #it does not need to be rendered.
        whichGrid = self.findAdjacency(shipGrid)
        
        if whichGrid != None:
            #Then we need to render the object to the screen.
            #Add the relative grid (from whichGrid) to the ship's grid
            #and the grid offset for the object.
            offset = (self.position[0] % 500 - self.rect.width / 2, self.position[1] % 500 - self.rect.height / 2)
            currentGridLoc = (currentGridLoc[0] + whichGrid[0] * 500 + offset[0], currentGridLoc[1] + whichGrid[1] * 500 + offset[1])
            
            #screen.blit(self.image, currentGridLoc)
            
            self.rect.x = currentGridLoc[0]
            self.rect.y = currentGridLoc[1]
             
            return self
        return None  
        
        
    def findAdjacency(self, shipGrid):
        #What we want to do is check the nine grids present
        #on the screen, and if the grid that the ship is
        #currently on is the same as or adjacent to the
        #object's grid, then return that adjustment.
        
        #This will return the grid to render the object to
        #with respect to the ship's grid
        for x in range(-1, 2):
            for y in range(-1, 2):
                if self.grid == (shipGrid[0] + x, shipGrid[1] + y):
                    return (x, y)
        return None
    
    def set_picked_up(self, ticks):
        self.picked_up = ticks
        self.kill()
        
    def is_consumed(self, current_ticks):
        # print((current_ticks - self.picked_up), self.life)
        
        return ((current_ticks - self.picked_up) > self.life)
    
class PowerUpShield(pygame.sprite.Sprite):
    def __init__(self, screen, spawnLoc):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = screen.load_image("powerUpShield.png")
        self.name = "shield"
        self.life = 30000 # 30 seconds
        self.picked_up = 0
        
        self.position = spawnLoc
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        
    def update(self, screen, shipPosition):
        currentGridLoc = (SCREENDIM.width / 2 - shipPosition[0] % 500, SCREENDIM.height / 2 - shipPosition[1] % 500)
        
        shipGrid = (int(shipPosition[0] / 500), int(shipPosition[1] / 500))
        
        #If the object is not adjacent to the player's grid,
        #it does not need to be rendered.
        whichGrid = self.findAdjacency(shipGrid)
        
        if whichGrid != None:
            #Then we need to render the object to the screen.
            #Add the relative grid (from whichGrid) to the ship's grid
            #and the grid offset for the object.
            offset = (self.position[0] % 500 - self.rect.width / 2, self.position[1] % 500 - self.rect.height / 2)
            currentGridLoc = (currentGridLoc[0] + whichGrid[0] * 500 + offset[0], currentGridLoc[1] + whichGrid[1] * 500 + offset[1])
            
            #screen.blit(self.image, currentGridLoc)
            
            self.rect.x = currentGridLoc[0]
            self.rect.y = currentGridLoc[1]
             
            return self
        return None  
        
        
    def findAdjacency(self, shipGrid):
        #What we want to do is check the nine grids present
        #on the screen, and if the grid that the ship is
        #currently on is the same as or adjacent to the
        #object's grid, then return that adjustment.
        
        #This will return the grid to render the object to
        #with respect to the ship's grid
        for x in range(-1, 2):
            for y in range(-1, 2):
                if self.grid == (shipGrid[0] + x, shipGrid[1] + y):
                    return (x, y)
        return None
    
    def set_picked_up(self, ticks):
        self.picked_up = ticks
        self.kill()
        
    def is_consumed(self, current_ticks):
        # print((current_ticks - self.picked_up), self.life)
        
        return ((current_ticks - self.picked_up) > self.life)
    
class Shield(pygame.sprite.Sprite):
    def __init__(self, screenDim, spawnLoc=[0, 0]):
        screen = Screen()
        pygame.sprite.Sprite.__init__(self)
        
        """Save a copy of the screen's rectangle"""
        self.screen = pygame.display.get_surface()
        
        """Create a variable to store the previous position of the sprite"""
        self.old = (0, 0, 0, 0)
        
        self.image, self.rect = screen.load_image("shield.png")
        self.original_image = self.image
        
        #NOTE: I had to make to two different positions for the ship,
        #one to render it to the center of the screen, and
        #one to render the correct relative background for the map.
        
        #Adjust the ship's position based off of
        #the screen's dimensions - the ship's dimensions (centered)
        shipDem = self.getDimensions()
        self.rect.x = screenDim[0] / 2 - shipDem[0] / 2
        self.rect.y = screenDim[1] / 2 - shipDem[1] / 2
        
        #Ship's position, determined by its spawn location
        #Measured at the center of the image!
        self.spawnLoc = spawnLoc
        self.screenDim = screenDim
        self.position = self.spawnLoc
        self.max_speed = 6
        
        #Grid information
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
        
        """Set initial speed, angle, etc"""
        self.angle = 0
        self.speed = 0
        
        #Pattern for regular expression used in displaying self.position
        self.pattern = re.compile('(\d+)(\.)*(\d+)*\, (\d+)(\.)*(\d+)*')
        
        self.powerUps = []
        self.gun_mode = "regular"
        
    
    def rotate(self, degrees):
        self.angle = (self.angle + degrees) % 360
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        
    def accelerate(self):
        self.speed += 0.5
        if(self.speed > self.max_speed):
            self.speed = self.max_speed
        
    def decelerate(self):
        self.speed -= 1
        if(self.speed < -self.max_speed):
            self.speed = -self.max_speed
                  
    #This function simply updates the ship's position/grid and
    #Ensures that the ship hasn't passed the map's borders
    def update(self, mapSize):
        
        scale_x = math.cos(math.radians(self.angle))
        scale_y = math.sin(math.radians(self.angle))
        
        velocity_x = (self.speed * scale_y)
        velocity_y = (self.speed * scale_x)
        
        self.position = [self.position[0] - velocity_x, self.position[1] - velocity_y]
        
        """Check to see if we are at the edge of the map"""
        #Left limit
        if self.position[0] < 0:
            self.position[0] = 0
        #Right limit
        elif self.position[0] > (mapSize[0] - self.rect.width):
            self.position[0] = mapSize[0] - self.rect.width
        #Top limit
        if self.position[1] < 0:
            self.position[1] = 0
        #Bottom limit
        elif self.position[1] > (mapSize[1] - self.rect.height):
            self.position[1] = mapSize[1] - self.rect.height
        
        #Update grid information
        self.grid = (int(self.position[0] / 500), int(self.position[1] / 500))
            
    #Return (Width, Height) of the player's ship
    def getDimensions(self):
        return (self.rect.width, self.rect.height)
    
    #Change ship's spawn point
    def changeSpawn(self, spawnLoc):
        self.spawnLoc = spawnLoc
        return True
    
    def respawn(self):
        #Reset values
        self.position = self.spawnLoc
        self.speed = 0
        self.angle = 0
        #Changes in angle will not appear unless rotate is called afterward
        self.rotate(0)
        
    
    #Display data onscreen (useful for debugging, etc.)
    def displayData(self, baseCollide):
        if pygame.font:
            font = pygame.font.Font(None, 20)
            #Position
            position = str(self.position)
            position = (int(self.pattern.search(position).group(1)), int(self.pattern.search(position).group(4)))
            
            text = font.render(str(position), 1, (255, 255, 255))
            textpos = text.get_rect(right=self.screen.get_width())
            self.screen.blit(text, textpos)
            #Grid
            text = font.render(str(self.grid), 1, (255, 255, 255))
            textpos = text.get_rect(right=self.screen.get_width(), top=font.get_linesize())
            self.screen.blit(text, textpos)
            #Collide
            if baseCollide == True:
                text = font.render("Spawn point set", 1, (255, 255, 255))
                textpos = text.get_rect(right=self.screen.get_width(), top=font.get_linesize()*2)
                self.screen.blit(text, textpos)
            return True
        else:
            return False
